import Konva from "konva";
import { KonvaGraph } from "./KonvaGraph";
import { ColorScheme } from "./ColorScheme";
import { Command } from "../Command/Command";
import { LayerManager } from "./LayerManager";
import { Dialog } from "../Components/Dialog";
import { Loading } from "../Components/Loding";
import { TopMenu } from "../Components/TopMenu";
import { Message } from "../Components/Message";
import { getUniqueId, nextTick } from "../Utils";
import { KonvaEvent } from "../Event/KonvaEvent";
import { MenuMask } from "../Components/MenuMask";
import { PreviewManager } from "./PreviewManager";
import { SlideAnimation } from "./SlideAnimation";
import { ShapeAnimation } from "./ShapeAnimation";
import { GlobalEvent } from "../Event/GlobalEvent";
import { YjsSendData } from "../Interface/Websocket";
import { EventBus } from "../Event/eventbus/EventBus";
import { FooterMenu } from "../Components/FooterMenu";
import { YjsWebsocket } from "../Websocket/Websocket";
import { CommandAdapt } from "../Command/CommandAdapt";
import { EventBusMap } from "../Interface/EventBusMap";
import { ContextMenu } from "../Components/ContextMenu";
import { FrameSelect } from "../Components/FrameSelect";
import { IDrawOptions, IRender } from "../Interface/Pptx";
import { ShapeSettings } from "../Components/ShapeSettings";
import { VirtualScroll } from "../Components/VirtualScroll";
import { KonvaTextInput } from "../Components/KonvaTextInput";
import { getContainer, getKonvaBoxSize } from "../Utils/konva";
import { MiddleContainer } from "../Components/MiddleContainer";
import { HistoryManager } from "../HistoryManager/HistoryManager";
import { DEFAULT_SLIDE_OPTIONS } from "../Config/default-slide-options";

export class Draw {
  private kpid: string; // 当前实例 唯一ID
  private dialog: Dialog; // 全局弹窗
  private loading: Loading; // 全局 loading
  private command: Command; // 全局 Command
  private message: Message; // 消息提示
  private menuMask: MenuMask; // 菜单蒙版
  private stage!: Konva.Stage; // 编辑区全局唯一 stage
  private rootSelector: string; // 根节点选择器
  private konvaGraph: KonvaGraph; // konva 图形对象（需要重写相关方法，以支持拖拽缩放等功能）
  private konvaEvent: KonvaEvent; // konva 全局事件
  private contentMenu: ContextMenu; // 右键菜单类
  private colorScheme: ColorScheme; // 主题类（幻灯片主题色）
  private globalEvent: GlobalEvent; // window 全局事件
  private layer!: Konva.Layer | null; // 始终指向当前编辑区
  private layerManager: LayerManager; // 图层管理器
  private shapeSettings!: ShapeSettings; // 图形设置面板
  private previewManager: PreviewManager; // 预览管理器
  private slideAnimation: SlideAnimation; // 幻灯片切换动画
  private shapeAnimation: ShapeAnimation; // 图形动画
  private historyManager: HistoryManager; // 历史记录管理器
  private eventbus: EventBus<EventBusMap>; // 全局事件总线
  private websocket: YjsWebsocket | null = null; // 初始化协同

  constructor(options: IDrawOptions) {
    this.kpid = getUniqueId(); // 标记全局唯一ID(konva ppt ID)
    this.eventbus = options.eventbus;
    this.rootSelector = `.konva-root[kpid="${this.kpid}"]`;
    this.message = new Message(); // 创建消息提示
    this.loading = new Loading(); // 创建全局 loading
    this.dialog = new Dialog(this); // 全局弹窗
    this.menuMask = new MenuMask(this); // 创建菜单蒙版
    this.colorScheme = new ColorScheme(this); // 创建配色方案类,在 KonvaGraph 前创建
    this.konvaGraph = new KonvaGraph(this); // 创建图形对象
    this.konvaEvent = new KonvaEvent(this); // 创建 Konva 事件
    this.contentMenu = new ContextMenu(this); // 邮件菜单
    this.globalEvent = new GlobalEvent(this); // 创建全局事件
    this.layerManager = new LayerManager(this); // 创建图层管理器
    this.previewManager = new PreviewManager(this); // 创建预览管理器
    this.slideAnimation = new SlideAnimation(this); // 创建幻灯片动画
    this.shapeAnimation = new ShapeAnimation(this); // 创建幻图形动画
    this.historyManager = new HistoryManager(this); // 创建历史记录管理器
    this.command = new Command(new CommandAdapt(this)); // 创建 Command API
    if (options.websocket) this.createWebsocket(options); // 创建 websocket 链接

    // 初始化完基础配置信息后，创建主容器
    this.createContainer(options);
  }

  /**
   * 根据提供的 container 创建容器结构:
   *  1. topmenu
   *  2. middle-container => thumb + konvabox
   *  3. footer
   */
  private createContainer(options: IDrawOptions) {
    // 解析用户传入的容器对象
    const root = getContainer(options.container);

    // 在用户容器对象内，创建konva-root根容器，并设置唯一ID
    const konvaroot = document.createElement("div");
    konvaroot.className = "konva-root";
    konvaroot.setAttribute("kpid", this.kpid);
    root.appendChild(konvaroot);

    // 创建组件
    new TopMenu(this); // 顶部菜单
    new MiddleContainer(this); // 中间容器
    this.shapeSettings = new ShapeSettings(this); // 创建图形设置面板
    new FooterMenu(this); // 底部菜单

    // 创建 konva
    this.createKonva(options.width, options.height);
  }

  /**
   * 创建 konva stage
   */
  private createKonva(w?: number, h?: number) {
    // 这里的 container 就是 konva 容器，在当前实例（rootBox[kpid]）查找
    const rootBox = this.getRootBox();
    const konvaBoxSelector = ".konva-root-container-konvabox";
    const container = <HTMLDivElement>rootBox.querySelector(konvaBoxSelector);

    // 处理宽高（始终保持 16：9 即可，同时，固定的偏移量也在这函数内设置 offset）
    const { width, height } = getKonvaBoxSize(container, w, h);
    const stageOption = { container, width, height };
    this.stage = new Konva.Stage(stageOption);

    // 创建虚拟滚动
    new VirtualScroll(this);

    // 这里还需要创建一个 konva 文本输入编辑器 contenteditable box，用于处理双击文本编辑
    new KonvaTextInput(this);

    // 创建一个选区，用于实现框选功能
    new FrameSelect(this);

    // 初始化 konvaEvent 事件
    this.konvaEvent.initKonvaEvent();

    // 初始化第一张幻灯片提示
    this.createDefaultSlide();

    this.demo();

    // 关闭 loading 提示
    this.loading.hide();

    // loaded
    nextTick(() => {
      // eslint-disable-next-line
      // @ts-ignore
      const version = import.meta.env.VITE__UNI_PPTX_VERSION__;
      // eslint-disable-next-line
      // @ts-ignore
      const lastUpdate = import.meta.env.VITE__UNI_PPTX_LAST_UPDATE__;

      console.group("## ==> Uni PPTX 应用加载完成:");
      console.log("version: %c" + version, "color: #fff; background: #FF5E01;");
      console.log(
        "lastUpdate: %c" + lastUpdate,
        "color: #fff; background: #FF5E01;"
      );
      console.groupEnd();

      if (this.eventbus.isSubscribe("loaded"))
        this.eventbus.emit("loaded", {
          version,
          lastUpdate,
        });
    });
  }

  /**
   * 创建一个默认的幻灯片 (单击此处添加新幻灯片)
   */
  public async createDefaultSlide() {
    const layer = new Konva.Layer({ id: "defaultSlide" });
    const group = new Konva.Group({ x: 0, y: 0 });
    const { width, height } = this.stage.size();

    const { rectoption, textOption } = DEFAULT_SLIDE_OPTIONS(width, height);

    // 创建矩形
    const rect = new Konva.Rect(rectoption);
    // 创建文字
    const text = new Konva.Text(textOption);
    // 偏移居中
    text.offset({ x: text.width() / 2, y: text.height() / 2 });
    // 链式调用添加原件
    this.stage.add(layer.add(group.add(rect, text)));

    // 添加事件
    group.on("click", (e) => {
      e.cancelBubble = true; // 阻止默认事件冒泡
      // 1. 先清空 layer
      this.clearLayer();
      // 2. 添加一张新的幻灯片
      this.addSlide();
    });
  }

  /** 添加幻灯片 */
  public addSlide() {
    // 创建新幻灯片之前，需要进行图层管理
    this.layerManager.cacheLayer();
    this.clearLayer();

    const layerID = getUniqueId();
    const layer = new Konva.Layer({ id: layerID });
    const currentIndex = this.layerManager.getCurrentIndex();

    // 新建幻灯片协同
    this.broadcast({
      type: `${layerID}_newSlide`,
      payload: { layerID, order: currentIndex },
    });

    this.addSlideToEditor(layer);

    // 判断是否有全局切换动画
    nextTick(() => this.slideAnimation.triggerGlobalAnimation());
  }

  /** 将创建的幻灯片添加到编辑器 */
  public addSlideToEditor(layer: Konva.Layer) {
    if (this.stage.children[0]?.id() === "defaultSlide") this.clearLayer();

    this.layer = layer;

    // 更新背景
    this.layerManager.setBackgroundLayer();

    // 添加到stage ==> 执行这步，页面才可编辑
    this.stage.add(this.layer!);

    // 渲染
    this.render();

    // 将幻灯片缩略图列表滚动条置底
    const root = this.getRootBox();
    const parent = root!.querySelector(".konva-root-container-thumb")!;
    parent.scrollTop = parent.scrollHeight;
  }

  // 创建 websocket
  private createWebsocket(options: IDrawOptions) {
    if (!options.websocket) return;
    this.websocket = new YjsWebsocket(options.websocket, this); // 创建 websocket
  }

  // 广播 websocket
  public broadcast(data: YjsSendData) {
    if (!this.websocket) return;
    this.websocket.send(data);
  }

  /** 清空所有的 layer */
  public clearLayer() {
    this.stage.getLayers()!.forEach((layer) => layer.destroy());
  }

  /** 清空所有的形变节点 */
  public clearTransformer() {
    this.stage.find("Transformer").forEach((tr) => tr.destroy());
  }

  /** 清空所有 line 控制点 */
  public clearLineControl() {
    const layer = this.layer;
    if (!layer) return;
    layer.find(".line-control").forEach((control) => control.destroy());
    this.clearLineSelected();
  }

  /** 清空线段选中状态 */
  public clearLineSelected() {
    const selected = this.konvaGraph.getLineSelected();
    selected.forEach((g) => g.setAttrs({ selected: null }));
  }

  /** 删除节点选中状态 */
  public clearSelected() {
    const selected = this.konvaGraph.getSelected();
    selected.forEach((g) => g.setAttrs({ selected: null }));
  }

  /** 重新渲染 - 需要更新 stage、 cacheLayer、LayerImage、history */
  public render(payload?: IRender) {
    const {
      redraw = true,
      cacheLayer = true,
      layerImage = true,
      patchHistory = true,
    } = payload || {};

    if (redraw) this.stage.draw(); // 重新渲染
    if (cacheLayer) this.layerManager.cacheLayer(); // 缓存Layer
    if (layerImage) this.layerManager.getLayerImage(); // 重新渲染缩略图
    if (patchHistory) this.historyManager.patchHistory(); // 执行历史记录
  }

  /** 销毁整个编辑器 */
  public destroy() {
    this.globalEvent.removeEvent();
    this.konvaEvent.removeEvent();
    this.getRootBox().remove(); // 销毁DOM
    this.websocket?.close(); // 销毁 websocket
    nextTick(() => {
      if (this.eventbus.isSubscribe("destroyed"))
        this.eventbus.emit("destroyed"); // 触发事件
    });
  }

  /** setter */
  public setLayer = (layer: Konva.Layer | null) => (this.layer = layer);

  /** getter */
  public getStage = () => this.stage;
  public getLayer = () => this.layer;
  public getDialog = () => this.dialog;
  public getCommand = () => this.command;
  public getMessage = () => this.message;
  public getLoading = () => this.loading;
  public getMenuMask = () => this.menuMask;
  public getEventBus = () => this.eventbus;
  public getKonvaEvent = () => this.konvaEvent;
  public getKonvaGraph = () => this.konvaGraph;
  public getColorScheme = () => this.colorScheme;
  public getContentMenu = () => this.contentMenu;
  public getLayerManager = () => this.layerManager;
  public getShapeSettings = () => this.shapeSettings;
  public getPreviewManager = () => this.previewManager;
  public getHistoryManager = () => this.historyManager;
  public getSlideAnimation = () => this.slideAnimation;
  public getShapeAnimation = () => this.shapeAnimation;
  public getRootBox = () => document.querySelector(this.rootSelector)!;

  /** demo 测试用例 */
  private demo() {
    // 1. 创建一个空幻灯片
    // this.addSlide();
    // // // // 创建矩形
    // const rect = this.konvaGraph.Rect({
    //   width: 100,
    //   height: 100,
    //   x: 100,
    //   y: 100,
    // });
    // this.layer?.add(rect);

    // // 设置选中
    // setGroupTransformer(this, rect);

    // // 打开动画属性面板
    // this.shapeSettings.open(2);
    this.render();
  }
}
